package com.fanqwz.kotlinmvvm.common.util.log

import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.Protocol
import okhttp3.Response
import okhttp3.internal.http.HttpHeaders
import okio.Buffer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.EOFException
import java.io.IOException
import java.nio.charset.Charset
import java.nio.charset.UnsupportedCharsetException
import java.util.concurrent.TimeUnit


/**
 * Created by fanqwz on 2017/9/13.
 */

class HttpLogger : Interceptor {
    private val logger: Logger by lazy { LoggerFactory.getLogger(this.javaClass.simpleName) }

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
//        if (!BuildConfig.DEBUG) {
//            return chain.proceed(request);
//        }

        val logBody = true

        val requestBody = request.body()
        val hasRequestBody = requestBody != null

        val connection = chain.connection()
        val protocol = if (connection != null) connection.protocol() else Protocol.HTTP_1_1
        var requestStartMessage = "--> ${request.method()} ${request.url()} $protocol"
        if (hasRequestBody) {
            requestStartMessage += " (${requestBody!!.contentLength()}-byte body)"
        }
        logger.trace(requestStartMessage)


        if (!logBody || !hasRequestBody) {
            logger.trace("--> END " + request.method())
        } else if (bodyEncoded(request.headers())) {
            logger.trace("--> END " + request.method() + " (encoded body omitted)")
        } else {
            val buffer = Buffer()
            requestBody!!.writeTo(buffer)

            var charset: Charset? = UTF8
            val contentType = requestBody.contentType()
            if (contentType != null) {
                charset = contentType.charset(UTF8)
            }

            logger.trace("")
            if (isPlaintext(buffer) && buffer.size() < 2048) {
                logger.trace(buffer.readString(charset!!))
                logger.trace("--> END ${request.method()} (${requestBody.contentLength()}-byte body)")
            } else {
                logger.trace("--> END ${request.method()} (binary ${requestBody.contentLength()}-byte body omitted)")
            }
        }


        val startNs = System.nanoTime()
        var response: Response
        try {
            response = chain.proceed(request)
        } catch (e: IOException) {
            logger.error("<-- HTTP FAILED: ${e.message}")
            throw e
        }

        val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)

        val responseBody = response.body()
        val contentLength = responseBody!!.contentLength()
        val bodySize = if (contentLength != -1L) "${contentLength.toString()}-byte" else "unknown-length"
        logger.trace("<-- ${response.code()} ${response.message()}' ${response.request().url()} (${tookMs}ms)'")


        if (!logBody || !HttpHeaders.hasBody(response)) {
            logger.trace("<-- END HTTP")
        } else if (bodyEncoded(response.headers())) {
            logger.trace("<-- END HTTP (encoded body omitted)")
        } else {
            val source = responseBody.source()
            source.request(java.lang.Long.MAX_VALUE) // Buffer the entire body.
            val buffer = source.buffer()

            var charset: Charset? = UTF8
            val contentType = responseBody.contentType()
            if (contentType != null) {
                try {
                    charset = contentType.charset(UTF8)
                } catch (e: UnsupportedCharsetException) {
                    logger.trace("")
                    logger.trace("Couldn't decode the response body; charset is likely malformed.")
                    logger.trace("<-- END HTTP")

                    return response
                }

            }

            if (!isPlaintext(buffer)) {
                logger.trace("")
                logger.trace("<-- END HTTP (binary ${buffer.size()}-byte body omitted)")
                return response
            }

            if (contentLength != 0L) {
                logger.trace("")
                logger.trace(buffer.clone().readString(charset!!))
            }

            logger.trace("<-- END HTTP (${buffer.size()}-byte body)")
        }


        return response
    }

    private fun bodyEncoded(headers: Headers): Boolean {
        val contentEncoding = headers.get("Content-Encoding")
        return contentEncoding != null && !contentEncoding.equals("identity", ignoreCase = true)
    }

    companion object {
        private val UTF8 = Charset.forName("UTF-8")

        internal fun isPlaintext(buffer: Buffer): Boolean {
            try {
                val prefix = Buffer()
                val byteCount = if (buffer.size() < 64) buffer.size() else 64
                buffer.copyTo(prefix, 0, byteCount)
                for (i in 0..15) {
                    if (prefix.exhausted()) {
                        break
                    }
                    val codePoint = prefix.readUtf8CodePoint()
                    if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                        return false
                    }
                }
                return true
            } catch (e: EOFException) {
                return false // Truncated UTF-8 sequence.
            }

        }
    }

}
